package com.basker.pisces.domain.command.run;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.repository.IDataRepository;
import com.basker.pisces.domain.command.CommandResult;
import com.basker.pisces.domain.command.annotation.Command;
import com.basker.pisces.domain.command.meta.DeleteCommand;
import com.basker.pisces.domain.command.meta.DeleteCommand.DeleteMode;
import com.basker.pisces.domain.constant.DomainConstants.DataStatus;
import com.basker.pisces.domain.property.meta.StatusProperty;
import com.basker.pisces.domain.validation.validator.ICommandValidator;
import com.basker.pisces.domain.validation.validator.ValidStatusValidator;

@Command
public class DeleteCommandRunner extends TransactionalCommandRunner<DeleteCommand> {

    @Autowired
    private IDataRepository repository;

    @Override
    protected List<ICommandValidator> getInnerValidators() {
        List<ICommandValidator> validators = super.getInnerValidators();

        DeleteCommand command = this.getCommand();
        if (this.isChangeStatus() && !CollectionUtils.isEmpty(command.getValidStatuses())) {
            validators = new ArrayList<>(validators);
            validators
                    .add(new ValidStatusValidator(command.getStatusPropertyAlias(), command.getValidStatuses(), null));
        }

        return validators;
    }

    @Override
    protected CommandResult transactionalInvoke(Object[] dataObjects) {
        if (isChangeStatus()) {
            this.doChangeStatus(dataObjects);
        } else {
            this.doDelete(dataObjects);
        }

        return CommandResult.ok(null);
    }

    private void doChangeStatus(Object[] dataObjects) {
        StatusProperty statusProperty = this.getStatusProperty();

        for (Object dataObject : dataObjects) {
            statusProperty.setValue(dataObject, DataStatus.DELETE);
        }

        IDataObjectMeta objectMeta = this.getContext().getEntity().getDataObjectMeta();
        this.repository.saveBatch(objectMeta, dataObjects);
    }

    private void doDelete(Object[] dataObjects) {
        List<Object> idValues = this.resolveIdValues(dataObjects);
        IDataObjectMeta objectMeta = this.getContext().getEntity().getDataObjectMeta();
        this.repository.deleteBatch(objectMeta, idValues.toArray());
    }

    private StatusProperty getStatusProperty() {
        return (StatusProperty) this.getContext().getEntity()
                .getPropertyByAlias(this.getCommand().getStatusPropertyAlias(), true);
    }

    private boolean isChangeStatus() {
        return DeleteMode.ChangeStatus.equals(this.getCommand().getMode());
    }

    private List<Object> resolveIdValues(Object[] dataObjects) {
        List<Object> idValues = new ArrayList<>(dataObjects.length);

        for (Object dataObject : dataObjects) {
            if (dataObject instanceof Number || dataObject instanceof String) {
                idValues.add(dataObject);
            } else {
                Object idValue = this.getContext().getEntity().getIdValue(dataObject);
                idValues.add(idValue);
            }
        }

        return idValues;
    }

}
